package com.nebula.core.dsl.operation;

import com.google.common.collect.ArrayListMultimap;
import com.nebula.core.entity.vo.FieldDesc;
import com.nebula.core.enums.AlterMethod;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;

/**
 * Tag操作类
 *
 * @author zhangtao
 */
public class SchemaOperation {
    /**
     * 创建标签
     * CREATE TAG [IF NOT EXISTS] <tag_name>
     * (
     * <prop_name> <data_type> [NULL | NOT NULL] [DEFAULT <default_value>] [COMMENT '<comment>']
     * [{, <prop_name> <data_type> [NULL | NOT NULL] [DEFAULT <default_value>] [COMMENT '<comment>']} ...]
     * )
     * [TTL_DURATION = <ttl_duration>]
     * [TTL_COL = <prop_name>]
     * [COMMENT = '<comment>'];
     *
     * @param tagName
     * @param fieldDescList
     * @return
     */
    protected static String createTag(String tagName, List<FieldDesc> fieldDescList) {
        if (StringUtils.isBlank(tagName)) {
            throw new RuntimeException("tagName is not empty");
        }
        if (CollectionUtils.isEmpty(fieldDescList)) {
            throw new RuntimeException("fieldDescList is not empty");
        }
        return createSchema("TAG", tagName, fieldDescList);
    }

    /**
     * 删除标签
     * DROP TAG [IF EXISTS] <tag_name>;
     *
     * @param tagName
     * @return
     */
    protected static String dropTag(String tagName) {
        if (StringUtils.isBlank(tagName)) {
            throw new RuntimeException("tagName is not empty");
        }
        return dropSchema("TAG", tagName);
    }

    /**
     * 更改Tag
     * ALTER TAG <tag_name>
     * <alter_definition> [, alter_definition] ...]
     * [ttl_definition [, ttl_definition] ... ]
     * [COMMENT = '<comment>'];
     * alter_definition:
     * | ADD (prop_name data_type)
     * | DROP (prop_name)
     * | CHANGE (prop_name data_type)
     * ttl_definition:
     * TTL_DURATION = ttl_duration, TTL_COL = prop_name
     *
     * @param tagName
     * @param fieldDescList
     * @return
     */
    protected static String alterTag(String tagName, List<FieldDesc> fieldDescList) {
        if (StringUtils.isBlank(tagName)) {
            throw new RuntimeException("tagName is not empty");
        }

        if (CollectionUtils.isEmpty(fieldDescList)) {
            throw new RuntimeException("fieldDescList is not empty");
        }
        return alterSchema("TAG", tagName, fieldDescList);
    }


    /**
     * 创建边类型
     * CREATE EDGE [IF NOT EXISTS] <edge_type_name>
     * (
     * <prop_name> <data_type> [NULL | NOT NULL] [DEFAULT <default_value>] [COMMENT '<comment>']
     * [{, <prop_name> <data_type> [NULL | NOT NULL] [DEFAULT <default_value>] [COMMENT '<comment>']} ...]
     * )
     * [TTL_DURATION = <ttl_duration>]
     * [TTL_COL = <prop_name>]
     * [COMMENT = '<comment>'];
     *
     * @param edgeType
     * @param fieldDescList
     * @return
     */
    protected static String createEdge(String edgeType, List<FieldDesc> fieldDescList) {
        if (StringUtils.isBlank(edgeType)) {
            throw new RuntimeException("edgeType is not empty");
        }
        if (CollectionUtils.isEmpty(fieldDescList)) {
            throw new RuntimeException("fieldDescList is not empty");
        }
        return createSchema("EDGE", edgeType, fieldDescList);
    }

    /**
     * 删除标签
     * DROP EDGE [IF EXISTS] <edge_type_name>
     *
     * @param edgeType
     * @return
     */
    protected static String dropEdge(String edgeType) {
        if (StringUtils.isBlank(edgeType)) {
            throw new RuntimeException("edgeType is not empty");
        }
        return dropSchema("EDGE", edgeType);
    }

    /**
     * 更改Edge
     * ALTER EDGE <edge_type_name>
     * <alter_definition> [, alter_definition] ...]
     * [ttl_definition [, ttl_definition] ... ]
     * [COMMENT = '<comment>'];
     * alter_definition:
     * | ADD (prop_name data_type)
     * | DROP (prop_name)
     * | CHANGE (prop_name data_type)
     * ttl_definition:
     * TTL_DURATION = ttl_duration, TTL_COL = prop_name
     *
     * @param edgeType
     * @param fieldDescList
     * @return
     */
    protected static String alterEdge(String edgeType, List<FieldDesc> fieldDescList) {
        if (StringUtils.isBlank(edgeType)) {
            throw new RuntimeException("edgeType is not empty");
        }

        if (CollectionUtils.isEmpty(fieldDescList)) {
            throw new RuntimeException("fieldDescList is not empty");
        }
        return alterSchema("EDGE", edgeType, fieldDescList);
    }


    /**
     * 创建schema
     *
     * @param type
     * @param name
     * @param fieldDescList
     * @return
     */
    private static String createSchema(String type, String name, List<FieldDesc> fieldDescList) {
        StringBuilder ngql = new StringBuilder("CREATE ");
        ngql.append(type);
        ngql.append(" IF NOT EXISTS ");
        ngql.append(name);

        List<String> columns = new ArrayList<>();
        for (FieldDesc fieldDesc : fieldDescList) {
            StringBuilder fieldBuilder = new StringBuilder();
            fieldBuilder.append(fieldDesc.getPropName());
            fieldBuilder.append(" ");
            fieldBuilder.append(fieldDesc.getDataType());
            fieldBuilder.append(" ");
            if (fieldDesc.isNullable()) {
                fieldBuilder.append(" NULL ");
            } else {
                fieldBuilder.append(" NOT NULL ");
            }

            if (StringUtils.isNotBlank(fieldDesc.getDefaultValue())) {
                fieldBuilder.append(" DEFAULT ");
                fieldBuilder.append(fieldDesc.getDefaultValue());
            }

            if (StringUtils.isNotBlank(fieldDesc.getComment())) {
                fieldBuilder.append(" COMMENT ");
                fieldBuilder.append("'" + fieldDesc.getComment() + "'");
            }

            columns.add(fieldBuilder.toString());
        }

        StringJoiner columsJoin = new StringJoiner(",", "(", ")");
        columns.forEach(c -> columsJoin.add(c));
        ngql.append(columsJoin);

        return ngql.toString();
    }

    /**
     * 删除schema
     *
     * @param type
     * @param name
     * @return
     */
    private static String dropSchema(String type, String name) {
        StringBuilder ngql = new StringBuilder("DROP ");
        ngql.append(type);
        ngql.append(" IF EXISTS ");
        ngql.append(name);
        return ngql.toString();
    }

    /**
     * 更改schema
     *
     * @param type
     * @param name
     * @param fieldDescList
     * @return
     */
    protected static String alterSchema(String type, String name, List<FieldDesc> fieldDescList) {
        if (CollectionUtils.isEmpty(fieldDescList)) {
            throw new RuntimeException("fieldDescList is not empty");
        }

        StringBuilder ngql = new StringBuilder("ALTER ");
        ngql.append(type);
        ngql.append(" ");
        ngql.append(name);

        ArrayListMultimap<AlterMethod, String> map = ArrayListMultimap.create();
        for (FieldDesc fieldDesc : fieldDescList) {
            StringBuilder fieldBuilder = new StringBuilder();
            fieldBuilder.append(fieldDesc.getPropName());
            if (!AlterMethod.DROP.equals(fieldDesc.getAlterMethod())) {
                fieldBuilder.append(" ");
                fieldBuilder.append(fieldDesc.getDataType());
            }
            map.put(fieldDesc.getAlterMethod(), fieldBuilder.toString());
        }

        StringJoiner methodJoin = new StringJoiner(",", " ", " ");
        map.asMap().entrySet().stream()
                .forEach((e) -> {
                            StringJoiner columsJoin = new StringJoiner(",", "(", ")");
                            e.getValue().forEach(c -> columsJoin.add(c));
                            methodJoin.add(e.getKey().toString() + columsJoin.toString());
                        }
                );
        ngql.append(methodJoin);

        return ngql.toString();
    }
}
